import Dep, { popTarget, pushTarget } from "./dep";
import { queueWatcher } from "./scheduler";
let id = 0;
class Watcher {
  constructor(vm, exprOrFn, cb, options) {
    this.vm = vm;

    this.exprOrFn = exprOrFn;
    this.cb = cb;
    // 是不是用户watcher
    this.user = !!options.user;
    this.options = options;
    this.id = id++;
    this.lazy = !!options.lazy; // 如果是计算属性，那么lazy: true   dirty = true
    this.dirty = this.lazy;

    this.deps = [];
    this.depsId = new Set();

    // 默认应该让exprOrFn执行  exprOrFn 方法做了什么。 ？  调用render(去vm上取值) get

    if (typeof exprOrFn === "string") {
      // 需要将表达式转换成函数
      this.getter = function () {
        // 当我数据取值时，会进行依赖收集
        // vm['a']['a']
        let path = exprOrFn.split(".");
        let obj = vm;
        for (let i = 0; i < path.length; i++) {
          obj = obj[path[i]];
        }
        return obj;
        // return vm[exprOrFn]; // getter方法
      };
    } else {
      this.getter = exprOrFn;
    }
    // 默认初始化 要取值
    // 第一次的value
    this.value = this.lazy ? undefined : this.get();
  }

  get() {
    // render取值  Object.defineProperty. get
    // 每个属性都可以收集自己的watcher
    // 一个属性可以对应多个watcher   一个watcher可以对应多个属性
    // Dep.target = thisWatcher
    pushTarget(this);
    // 新值
    let value = this.getter.call(this.vm);
    // Dep.target = null 如果Dep.target有值说明这个变量在模板中使用
    popTarget();
    return value;
  }

  addDep(dep) {
    let id = dep.id;
    // 保证 同个组件中通过watcher中多次引用同一个属性
    if (!this.depsId.has(id)) {
      this.depsId.add(id);
      this.deps.push(dep);
      dep.addSub(this);
    }
  }

  update() {
    // 每次更新时，存储当前watcher 然后等多次。先将watcher缓存下来， 将一伙watcher合并一次更新
    if (this.lazy) {
      this.dirty = true;
    } else {
      queueWatcher(this);
    }
    // this.get();
  }

  run() {
    // 后续要有其他功能
    let newValue = this.get();
    // newVal, this.value
    let oldValue = this.value;
    // 保证下一次更新时上一次最新值是下一次的老值
    this.value = newValue;
    if (this.user) {
      this.cb.call(this.vm, newValue, oldValue);
    }
  }

  evaluate() {
    this.dirty = false; // 表示取过值了
    this.value = this.get();
  }

  depend() {
    let i = this.deps.length;
    while (i--) {
      this.deps[i].depend(); // lastName firstName 收集渲染watcher
    }
  }
}

// watch 和dep
// 在vue中页面渲染时使用的属性，需要进行依赖收集，收集对象的渲染 watcher
// 渲染页面前，将会当前watcher放到Dep类上
// 取值时，给每个属性都加了个dep属性。 用于存储这个渲染watacher
// 每个属性可能对应多个视图 多个视图肯定多个watcher 一个属性要对应多个watcher
// dep.depend() => 通知dep存放watcher => Dep.target.addDep() => 通知watcehr存放dep
export default Watcher;
